/******************************************************************************
*  Copyright(c) 2021-2023 Sietium
*
*  @file:    lights.cpp
*  @brif:    lights cpp
*
*  @date:    25/10/2021
*  @Author:  bird.du
*
******************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <memory>
#include <math.h>
#include <vector>
#include <glad/gl-headers.h>
#define GLFW_INCLUDE_NONE
#include <GLFW/glfw3.h>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#include "vmath.h"

#include "node.h"
#include "glslprogram.h"
#include "objectfactory.h"
#include "model.h"
#include "texture.h"

enum Light_Type {
    Light_Cartoon = 0,
    Light_Diffuse,
    Light_Gouraud,
    Light_Phong,
    Light_Blinn_Phong,
};

static const GLubyte toonTexData[] = {
    100, 100, 100, 0,
    200, 200, 200, 0,
    255, 255, 255, 0,
};

inline GLfloat trunCateDeg(GLfloat deg)
{
    if (deg >= 360.f)
        return deg - 360.f;
    else
        return deg;
}

class Lights : public Node
{
public:
    Lights()
    {
        nodeName = "Lights";
    }
protected:
    bool startup() override;
    void render(double currentTime, double difTime) override;
    void shutdown() override;
    void onResized(int width, int height)override;

private:
    GLSLProgram program;
    std::unique_ptr<Model> model;

    GLuint textureId = 0;
    GLint lightOperation = Light_Cartoon;
    double lightTimer = 0;
    float angle = 0.0f;

    const glm::vec3 lightColor = glm::vec3(1.0f, 1.0f, 1.0f);
    const glm::vec3 lightPos = glm::vec3(0.0f, 0.0f, 1000.0f);

    glm::vec3 viewPos = glm::vec3(0.0f, 0.0f, 3.0f);
    glm::vec3 cameraZAxis = glm::vec3(0.0f, 0.0f, -1.0f);
    glm::vec3 cameraXAxis = glm::vec3(1.0f, 0.0f, 0.0f);
    glm::vec3 cameraYAxis = glm::vec3(0.0f, 1.0f, 0.0f);
};

bool Lights::startup()
{
    if (PerfWindow::glVersion < 330) {
        printf("Failed! The GL version is less than 3.3 !\n");
        return false;
    }

    textureId = 0;
    lightOperation = Light_Cartoon;
    lightTimer = 0;
    angle = 0.0f;
    viewPos = glm::vec3(0.0f, 0.0f, 3.0f);
    cameraZAxis = glm::vec3(0.0f, 0.0f, -1.0f);
    cameraXAxis = glm::vec3(1.0f, 0.0f, 0.0f);
    cameraYAxis = glm::vec3(0.0f, 1.0f, 0.0f);

    bool ok = program.build("../media/shaders/lights/chicken_vs.glsl.txt", \
                            "../media/shaders/lights/chicken_fs.glsl.txt");
    if (!ok) {
        return false;
    }

    model.reset(new Model);
    model->loadModel("../media/objects/chicken.obj");
    model->setUseGLPath(GL_FALSE);

    if (textureId == 0) {
        glGenTextures(1, &textureId);
    }
    glBindTexture(GL_TEXTURE_2D, textureId);
    glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, sizeof(toonTexData) / 4, 1);
    glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, sizeof(toonTexData) / 4, 1, \
                    GL_RGBA, GL_UNSIGNED_BYTE, toonTexData);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    glBindTexture(GL_TEXTURE_2D, 0);

    glm::mat4 trans = glm::mat4(1.0);
    glm::mat4 view = glm::lookAt(viewPos, viewPos + glm::normalize(cameraZAxis), cameraYAxis);
    glm::mat4 projection = glm::mat4(1.0f);
    projection = glm::perspective(glm::radians(45.0f), \
                                  static_cast<float>(getWidth()) / static_cast<float>(getHeight()), \
                                  0.1f, 10000.0f);
    program.use();
    program.setUniformMatrix4fv("trans", glm::value_ptr(trans));
    program.setUniformMatrix4fv("view", glm::value_ptr(view));
    program.setUniformMatrix4fv("projection", glm::value_ptr(projection));
    program.setUniform3f("lightColor", lightColor.x, lightColor.y, lightColor.z);
    program.setUniform3f("lightPos", lightPos.x, lightPos.y, lightPos.z);
    program.setUniform3f("viewPos", viewPos.x, viewPos.y, viewPos.z);
    program.setUniform1i("texture02", 1);
    program.setUniform1i("useCartoon", static_cast<int>(lightOperation));
    lightTimer = glfwGetTime();
    angle = 0.0f;

    return true;
}

void Lights::render(double currentTime, double difTime)
{
    (void)currentTime;
    (void)difTime;
    glEnable(GL_DEPTH_TEST);
    glClearColor(0.6f, 0.6f, 0.6f, 0.6f);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    program.use();
    if ((glfwGetTime() - lightTimer) >= 4) {
        lightOperation++;
        if (lightOperation > Light_Blinn_Phong) {
            lightOperation = Light_Cartoon;
        }
        program.setUniform1i("useCartoon", lightOperation);
        lightTimer = glfwGetTime();
    }
    glm::mat4 trans = glm::mat4(1.0);
    angle = angle + static_cast<float>(30 * difTime);
    if (angle >= 360.0f) {
        angle = 0;
    }
    trans = glm::rotate(trans, glm::radians(angle), glm::vec3(0.0, 1.0, 0.0));
    program.setUniformMatrix4fv("trans", glm::value_ptr(trans));

    glActiveTexture(GL_TEXTURE1);
    glBindTexture(GL_TEXTURE_2D, textureId);
    model->renderModel();
    glBindTexture(GL_TEXTURE_2D, 0);
    glActiveTexture(GL_TEXTURE0);


}

void Lights::shutdown()
{
    program.clear();
    model->clearModel();
    glDeleteTextures(1, &textureId);
}

void Lights::onResized(int width, int height)
{
    glViewport(0, 0, width, height);
    Node::onResized(width, height);
}

REGISTER_OBJECT(Node, Lights)

